package calculate

import (
	"errors"
	"gitee.com/huanminabc/go-StrMathCalculate/register"
	"gitee.com/huanminabc/go-StrMathCalculate/structure"
	"gitee.com/huanminabc/go-utils/code_analysis"
	"gitee.com/huanminabc/go-utils/common"
	"strings"
)

// 按照优先级计算表达式,并返回结果
func CalculateExpression(expression string) string {

	//判断是否有()
	if strings.Index(expression, "(") == -1 || strings.Index(expression, ")") == -1 {
		//找不到括号,那么就直接计算
		return CalculateExpressionSplit(expression)
	}
	//判断()是否成对出现
	if strings.Count(expression, "(") != strings.Count(expression, ")") {
		panic("表达式中的()不成对出现")
	}
	//找到最后一个(,然后找到第一个),然后计算,然后替换
	lastIndex := strings.LastIndex(expression, "(")
	firstIndex := strings.Index(expression[lastIndex:], ")")
	if firstIndex == -1 {
		panic(errors.New("括号不匹配"))
	}

	//取出表达式
	newExpression := expression[lastIndex+1 : lastIndex+firstIndex]
	if newExpression == "" { //如果括号内表达式是空的那么直接计算
		return CalculateExpressionSplit(expression)
	}
	//计算括号内的表达式
	var result = CalculateExpressionSplit(newExpression)
	//实际值替换括号内的表达式
	expression = expression[:lastIndex] + result + expression[lastIndex+firstIndex+1:]
	//继续寻找括号
	return CalculateExpression(expression)
}
func CalculateSplit(expression string) []string {

	var isString = false
	//判断是存在字符串,将字符串替换成占位符
	var strMap = make(map[string]string)
	if strings.Contains(expression, "\"") {
		mapS, str := code_analysis.PickBoth(expression, '"')
		strMap = mapS
		expression = str
		isString = true
	}
	//判断是存在字符串,将字符串替换成占位符
	if strings.Contains(expression, "'") {
		mapS, str := code_analysis.PickBoth(expression, '\'')
		//合并
		for k, v := range mapS {
			strMap[k] = v
		}
		expression = str
		isString = true
	}
	//判断是否存在+号,如果存在,那么就是字符串拼接
	if isString {
		split := strings.Split(expression, "+")
		if len(split) > 1 {
			for _, v := range split {
				//判断是否存在占位符
				if strings.HasPrefix(v, "@@str") {
					continue
				}
				//给字符串加上占位符
				runes := "@@str@" + common.RandStringRunes()
				expression = strings.ReplaceAll(expression, v, runes)
				strMap[runes] = v
			}

		}
	}

	//将 Symbol中的符号倒序替换成占位符
	var symbolSlipMap = make(map[string]string)
	symbollen := len(register.SymbolSlip)
	for i := 0; i < symbollen; i++ {
		//单独处理!,因为!是单目运算符,所以需要特殊处理
		if register.SymbolSlip[i] == "!" {
			continue
		} else {
			//判断是否存在
			if strings.Contains(expression, register.SymbolSlip[i]) {
				runes := "@symbol@" + common.RandStringRunes()
				expression = strings.ReplaceAll(expression, register.SymbolSlip[i], " "+runes+" ") //留出空格,方便后面分割
				symbolSlipMap[runes] = register.SymbolSlip[i]
			}
		}

	}

	//将表达式按照空格分割
	split := strings.Split(expression, " ")
	//将分割占位符替换回来
	for i := range split {
		s, p := symbolSlipMap[split[i]]
		if p {
			split[i] = s
		}
	}
	//将字符串替换回来
	for i := range split {
		for k, v := range strMap {
			split[i] = strings.ReplaceAll(split[i], k, v)
		}
	}

	return split
}

// 计算表达式 1+2*3=7
func CalculateExpressionSplit(expression string) string {
	split := CalculateSplit(expression) //表达式拆分
	//处理0,2,4值中是否存在!,如果存在,那么就计算,然后替换
	for i := 0; i < len(split); i += 2 {
		if split[i][0] == '!' {
			//计算表达式
			calculateData := structure.CalculateData{
				A:      "",
				Symbol: "!",
				B:      split[i][1:],
			}
			//计算表达式
			result := calculateMode(calculateData)
			split[i] = result
		}
	}
	//按照优先级计算,如果没有优先相同的,那么就按照顺序计算
	for i := 1; i <= register.MaxPriority; i++ {
		var multi []int
		for f, s := range register.SymbolGroup[i] { //从表达式中找存在的符号,然后记录下标,如果存在多个符号,那么就表示有优先级相同的符号,那么就需要按照顺序计算
			if strings.Contains(expression, s) { //不包含符号,那么就跳过
				multi = append(multi, f)
			}
		}
		if len(multi) > 0 {
			if len(multi) == 1 { //只有一个符号,那么就直接计算
				for j := 1; j < len(split); j += 2 {
					if split[j] == register.SymbolGroup[i][multi[0]] { //找到符号
						//计算表达式
						calculateData := structure.CalculateData{
							A:      split[j-1],
							Symbol: split[j],
							B:      split[j+1],
						}
						//计算表达式
						result := calculateMode(calculateData)
						//替换表达式
						split[j-1] = result
						//删除表达式
						split = append(split[:j], split[j+2:]...)
						//重新计算
						j = -1
					}
				}

			} else { //多个符号,那么就按照顺序计算
				//按照顺序计算
				for j := 1; j < len(split); j += 2 {
					for _, k := range multi {
						if split[j] == register.SymbolGroup[i][k] { //找到符号
							//计算表达式
							calculateData := structure.CalculateData{
								A:      split[j-1],
								Symbol: split[j],
								B:      split[j+1],
							}
							//计算表达式
							result := calculateMode(calculateData)
							//替换表达式
							split[j-1] = result
							//删除表达式
							split = append(split[:j], split[j+2:]...)
							//重新计算
							j = -1
							break

						}
					}
				}

			}

		}

	}
	if len(split) == 0 {
		return ""
	}
	return split[0]
}

// 策略模式
func calculateMode(calculateData structure.CalculateData) string {
	for _, c := range register.Calculates {
		c.AddExpression(calculateData.A, calculateData.Symbol, calculateData.B)
		if c.IsBool(calculateData.Symbol) {
			c.Calculate()
			return c.GetResult()
		}
	}
	panic("没有找到对应的计算器->[ " + calculateData.Symbol + " ]请检查是否支持该计算器")
}

// 判断字符串中是否包含符号,如果包含,那么就是表达式,需要计算
func IsExpression(str1 string, symbol uint8) bool {
	_, str := code_analysis.PickBoth(str1, symbol) //取消字符串
	//判断是否是表达式,检测是否存在Symbol
	for i := range register.SymbolSlip {
		if strings.Contains(str, register.SymbolSlip[i]) {
			return true
		}
	}
	return false
}
